<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>2018/11/29</title>
    <!--好的代码像粥一样,是得用时间和精力熬的。-->
</head>
<body>
<script>
    //const声明一个只读的常量。一旦声明，常量的值就不能改变。
    // const PI = 3.1415;
    // console.log(PI); // 3.1415
    // PI = 3;
    // TypeError: Assignment to constant variable.

    //const声明的变量不得改变值，这意味着，const一旦声明变量，就必须立即初始化，不能留到以后赋值。
    //const foo;  //Missing initializer in const declaration
    //对于const来说，只声明不赋值，就会报错。

    //const的作用域与let命令相同：只在声明所在的块级作用域内有效。
    // if (true) {
    //     const MAX = 5;
    // }
    // console.log(MAX); // Uncaught ReferenceError: MAX is not defined

   // const命令声明的常量也是不提升，同样存在暂时性死区，
   // 只能在声明的位置后面使用。
   //  if (true) {
    //      console.log(MAX); //ReferenceError: MAX is not defined
    //      const MAX = 5;  //
    //  }

   //const声明的常量，也与let一样不可重复声明。
   //  var message = "Hello!";
   //  let age = 25;
    // 以下两行都会报错
    //const message = "Goodbye!";  //Identifier 'message' has already been declared
    //const age = 30;  //Identifier 'age' has already been declared


    // const foo = {};
    // // 为 foo 添加一个属性，可以成功
    // foo.prop = 123;
    // console.log(foo.prop); // 123
    // // 将 foo 指向另一个对象，就会报错
    // foo = {}; // TypeError: "foo" is read-only


    //常量a是一个数组，这个数组本身是可写的，
    // 但是如果将另一个数组赋值给a，就会报错。
    // const a = [];
    // a.push('Hello'); // 可执行
    // a.length = 0;    // 可执行
    // a = ['Dave'];    // 报错

    //如果真的想将对象冻结，应该使用Object.freeze方法。
    // const foo = Object.freeze({});
    // // 常规模式时，下面一行不起作用；
    // // 严格模式时，该行会报错
    // //添加新属性不起作用
    // foo.prop = 123;   //Identifier 'foo' has already been declared

    //对象本身冻结，对象的属性也应该冻结
    //对象彻底冻结的函数。

    var constantize = (obj) => {
        Object.freeze(obj);
        Object.keys(obj).forEach( (key, i) => {
                if ( typeof obj[key] === 'object' ) {
                    constantize( obj[key] );
                }
        });
    };

    //ES6 声明变量的六种方法
    //ES5 只有两种声明变量的方法：var命令和function命令。
    // ES6 除了添加let和const命令，后面章节还会提到，
    // 另外两种声明变量的方法：import命令和class命令。
    // 所以，ES6 一共有 6 种声明变量的方法。

//顶层对象的属性
    //顶层对象，在浏览器环境指的是window对象，在 Node 指的是global对象。
    // ES5 之中，顶层对象的属性与全局变量是等价的。
    //顶层对象的属性赋值与全局变量的赋值，是同一件事。
    //顶层对象的属性与全局变量挂钩，被认为是 JavaScript 语言最大的设计败笔之一。
    window.a = 1;
    console.log(a); // 1
    a = 2;
    console.log(window.a); // 2

    //var命令和function命令声明的全局变量，依旧是顶层对象的属性；
    // 另一方面规定，let命令、const命令、class命令声明的全局变量，不属于顶层对象的属性。
    //从 ES6 开始，全局变量将逐步与顶层对象的属性脱钩。
    var a = 1;
    // 如果在 Node 的 REPL 环境，可以写成 global.a
    // 或者采用通用方法，写成 this.a
    window.a // 1

    let b = 1;
    window.b // undefined

//global对象
    /*ES5 的顶层对象，本身也是一个问题，因为它在各种实现里面是不统一的。
     浏览器里面，顶层对象是window，但 Node 和 Web Worker 没有window。
     浏览器和 Web Worker 里面，self也指向顶层对象，但是 Node 没有self。
     Node 里面，顶层对象是global，但其他环境都不支持。
     同一段代码为了能够在各种环境，都能取到顶层对象，现在一般是使用this变量，但是有局限性。
     全局环境中，this会返回顶层对象。但是，Node 模块和 ES6 模块中，this返回的是当前模块。
     函数里面的this，如果函数不是作为对象的方法运行，而是单纯作为函数运行，this会指向顶层对象。但是，严格模式下，这时this会返回undefined。
     不管是严格模式，还是普通模式，new Function('return this')()，总是会返回全局对象。但是，如果浏览器用了 CSP（Content Security Policy，内容安全策略），那么eval、new Function这些方法都可能无法使用。
     综上所述，很难找到一种方法，可以在所有情况下，都取到顶层对象。下面是两种勉强可以使用的方法。
    * */
    // 方法一
    (typeof window !== 'undefined'
        ? window
        : (typeof process === 'object' &&
            typeof require === 'function' &&
            typeof global === 'object')
            ? global
            : this);

    // 方法二
    var getGlobal = function () {
        if (typeof self !== 'undefined') { return self; }
        if (typeof window !== 'undefined') { return window; }
        if (typeof global !== 'undefined') { return global; }
        throw new Error('unable to locate global object');
    };

    //垫片库system.global模拟了这个提案，可以在所有环境拿到global。
    //保证各种环境里面，global对象都是存在的。
    // CommonJS 的写法
    require('system.global/shim')();

    // ES6 模块的写法
    import shim from 'system.global/shim'; shim();


    //将顶层对象放入变量global。
    // CommonJS 的写法
    var global = require('system.global')();

    // ES6 模块的写法
    import getGlobal from 'system.global';
    const global = getGlobal();





</script>

</body>
</html>
